package us.wili.dev.client.util;

import org.apache.commons.lang3.StringUtils;
import us.wili.dev.client.jwt.JwtUser;
import us.wili.dev.common.exception.BaseException;
import us.wili.dev.common.exception.BaseExceptionEnum;
import us.wili.dev.exception.UserException;
import us.wili.dev.jwt.model.IUser;
import us.wili.dev.jwt.session.redis.RedisSession;

import java.util.function.Function;


public class JwtInfoUtils {

    private JwtInfoUtils(){}

    private static final ThreadLocal<RedisSession> JWT_SESSION_THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 默认过期时间15分钟
     */
    private static final Long CAPTCHA_EXPIRATION = 900L;
    /**
     * 短信验证码错误次数上限
     */
    private static final int CAPTCHA_MAX_ERROR_TIME = 5;
    /**
     * 生成获取验证码的key
     */
    private static final Function<String,String> GET_ERROR_TIMES_KEY = cacheKey -> cacheKey + ":errorTimes";

    /**
     * 设置当前jwt session
     */
    public static void setSession(RedisSession session){
        JWT_SESSION_THREAD_LOCAL.set(session);
    }

    public static RedisSession getSession() {
        return JWT_SESSION_THREAD_LOCAL.get();
    }

    /**
     * 取出当前jwt session会话用户
     * 用户如果是未登录状态的话会抛出异常提醒前端用户未登录
     * @return 会话用户
     */
    public static JwtUser getJwtUser() {
        RedisSession redisSession = JWT_SESSION_THREAD_LOCAL.get();
        ifAnnoymosFail();
        return (JwtUser) redisSession.getJwtUserInfo();
    }

    /**
     * @return 当前会话的用户的userId
     */
    public static long getUserId() {
        return getJwtUser().getUserId();
    }

    /**
     * 如果用户未登录抛出异常
     */
    public static void ifAnnoymosFail() {
        IUser jwtUserInfo = JWT_SESSION_THREAD_LOCAL.get().getJwtUserInfo();
        if (jwtUserInfo.getAnonymous()) {
            throw new UserException(BaseExceptionEnum.ANNOYMOUS);
        }
    }

    /**
     * 缓存验证码到会话缓存中,有效时间15分钟
     * @param captchaKey 缓存key
     * @param captcha  验证码
     */
    public static void cacheCaptcha(String captchaKey, String captcha) {
        cacheCaptcha(captchaKey, captcha, CAPTCHA_EXPIRATION);
    }

    /**
     * 缓存验证码到会话缓存中
     * @param captchaKey 缓存key
     * @param captcha 验证码
     * @param expiration 有效时间,单位秒
     */
    public static void cacheCaptcha(String captchaKey, String captcha, Long expiration) {
        RedisSession session = getSession();

        String errorTimesKey = captchaKey + ":errorTimes";

        session.removeAttribute(errorTimesKey);
        session.removeAttribute(captchaKey);

        session.setAttribute(captchaKey, captcha, expiration);
    }

    public static void verifyCaptcha(String cacheKey, String captcha) {
        RedisSession session = getSession();
        //获取缓存中的验证码
        String errorTimesKey = GET_ERROR_TIMES_KEY.apply(cacheKey);
        String captchaFromCache = session.getAttribute(cacheKey);

        if (!StringUtils.equals(captcha, captchaFromCache)) {
            int errorTimes = getErrorTimes(cacheKey);
            if (errorTimes < CAPTCHA_MAX_ERROR_TIME) {
                session.setAttribute(errorTimesKey, errorTimes);
                throw new BaseException(BaseExceptionEnum.CAPTCHA_ERROR.getMessage(), BaseExceptionEnum.CAPTCHA_ERROR.getCode());
            }else {
                session.removeAttribute(errorTimesKey);
                session.removeAttribute(cacheKey);
                throw new BaseException(BaseExceptionEnum.CAPTCHA_ERROR.getMessage(), BaseExceptionEnum.CAPTCHA_ERROR.getCode());
            }
        }

        // 删除验证码
        session.removeAttribute(errorTimesKey);
        session.removeAttribute(cacheKey);
    }

    /**
     * 根据key获取在会话缓存中的验证错误次数
     * @param cacheKey key
     */
    private static int getErrorTimes(String cacheKey) {
        String errorTimesKey = GET_ERROR_TIMES_KEY.apply(cacheKey);
        Integer errorTimes = getSession().getAttribute(errorTimesKey);
        errorTimes = errorTimes == null ? 1 : errorTimes + 1;

        return errorTimes;
    }

    /**
     * 客户端不要调用这个方法
     * 去除在这个threadlocal中的jwt 用户信息
     */
    public static void destory() {
        JWT_SESSION_THREAD_LOCAL.remove();
    }
}
